#ifndef MOVABLE_ENTITY_HPP
#define MOVABLE_ENTITY_HPP

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <string>

#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Sprite.hpp>

#include <SmegTools/Pattern/Smoother.hpp>
#include <SmegTools/Config.h>

#include "BaseEntity.hpp"
#include "../Game/MessageInfo.hpp"

#include <SmegTools/Debug/DebugNew.hpp>

namespace smeg {
    namespace core {
        class EntityManager;
        
////////////////////////////////////////////////////////////
/// Represents a placeable entity with an associated sprite
////////////////////////////////////////////////////////////
class MovableEntity : public BaseEntity {
public:
    ////////////////////////////////////////////////////////////
    /// Default Constructor
    ///
    ////////////////////////////////////////////////////////////
                                MovableEntity();
    ////////////////////////////////////////////////////////////
    /// Constructor with a texture
    ///
    /// \param _pTexture :      New texture
    ///
    ////////////////////////////////////////////////////////////
    explicit                    MovableEntity( const sf::Texture* _pTexture );
    ////////////////////////////////////////////////////////////
    /// Constructor with a texture and a rectangle
    ///
    /// \param _pTexture :      New texture
    /// \param _Rectangle :     Rectangle defining the region of the texture to display
    ///
    ////////////////////////////////////////////////////////////
                                MovableEntity( const sf::Texture* _pTexture, const sf::IntRect& _Rectangle );
    ////////////////////////////////////////////////////////////
    /// Copy Constructor
    ///
    /// \param _Copy :          Entity to copy from
    ///
    ////////////////////////////////////////////////////////////
                                MovableEntity( const MovableEntity& _Copy );
    ////////////////////////////////////////////////////////////
    /// Virtual Destructor
    ///
    ////////////////////////////////////////////////////////////
    virtual                     ~MovableEntity();

    DECLARE_CLONE_FUNC( MovableEntity, BaseObject )
        
    ////////////////////////////////////////////////////////////
    /// Update the entity
    ///
    /// \param _fDeltaTime :    Time since the last frame
    ///
    ////////////////////////////////////////////////////////////
    virtual void                Update( float _fDeltaTime );

    ////////////////////////////////////////////////////////////
    /// Handle An Incoming Message
    ///
    /// \param _Msg :           Message to handle
    ///
    ////////////////////////////////////////////////////////////
    virtual bool                HandleMessage( const MessageInfo& _Msg ) { return false; }
    
    /*********************** Accessors ************************/
    void                        SetVelocity( float _fX, float _fY );
    void                        SetVelocity( const sf::Vector2f& _Velocity );
    void                        AddVelocity( float _fX, float _fY );
    void                        AddVelocity( const sf::Vector2f& _Velocity );
    const sf::Vector2f&         GetVelocity() const;
    float                       GetSpeed() const;
    
    bool                        RotateHeadingToFacePosition( const sf::Vector2f& _TargetPos );
    void                        SetHeading( const sf::Vector2f& _Heading );
    const sf::Vector2f&         GetHeading() const;
    const sf::Vector2f&         GetSide() const;

    void                        SetMass( float _fMass );
    float                       GetMass() const;
    
    void                        SetMaxSpeed( float _fMaxSpeed );
    float                       GetMaxSpeed() const;
    float                       GetMaxSpeedSq() const;
    
    void                        SetMaxForce( float _fMaxForce );
    float                       GetMaxForce() const;
    float                       GetMaxForceSq() const;
    
    void                        SetMaxTurnRate( float _fMaxTurnRate );
    float                       GetMaxTurnRate() const;

    void                        SetTextureFollowDirection( bool _bTextureFollowDirection );
    
    void                        SetSmoothingOn( bool _IsSmoothingOn );
    bool                        IsSmoothingOn() const;

protected:                
    /****************** Protected functions *******************/
    void                        ConstrainVelocity();

    /******************* Protected members ********************/
    bool                        m_bTextureFollowDirection;  // Whether to rotate the texture as the direction changes

    sf::Vector2f                m_Velocity;
    sf::Vector2f                m_Heading;
    sf::Vector2f                m_Side;

    tools::Smoother< sf::Vector2f > m_HeadingSmoother;
    sf::Vector2f                m_SmoothedHeading;
    bool                        m_bIsSmoothingOn;

    float                       m_fMass;
    float                       m_fMaxSpeed;
    float                       m_fMaxSpeedSq;
    float                       m_fMaxForce;
    float                       m_fMaxForceSq;
    float                       m_fMaxTurnRate;
};

/************************** Inline ************************/
inline const sf::Vector2f& MovableEntity::GetVelocity() const {
    return m_Velocity;
}

inline float MovableEntity::GetSpeed() const {
    return math::Length( m_Velocity );
}

inline void MovableEntity::SetHeading( const sf::Vector2f& _Heading ) {
    math::NormalizedCopy( _Heading, m_Heading );
    math::PerpCopy( m_Heading, m_Side );
}

inline const sf::Vector2f& MovableEntity::GetHeading() const {
    return m_Heading;
}

inline const sf::Vector2f& MovableEntity::GetSide() const {
    return m_Side;
}

inline void MovableEntity::SetMass( float _fMass ) {
    m_fMass = _fMass;
}

inline float MovableEntity::GetMass() const {
    return m_fMass;
}

inline void MovableEntity::SetMaxSpeed( float _fMaxSpeed ) {
    m_fMaxSpeed     = _fMaxSpeed;
    m_fMaxSpeedSq   = m_fMaxSpeed * m_fMaxSpeed;
}

inline float MovableEntity::GetMaxSpeed() const {
    return m_fMaxSpeed;
}

inline float MovableEntity::GetMaxSpeedSq() const {
    return m_fMaxSpeedSq;
}

inline void MovableEntity::SetMaxForce( float _fMaxForce ) {
    m_fMaxForce     = _fMaxForce;
    m_fMaxForceSq   = m_fMaxForce * m_fMaxForce;
}

inline float MovableEntity::GetMaxForce() const {
    return m_fMaxForce;
}

inline float MovableEntity::GetMaxForceSq() const {
    return m_fMaxForceSq;
}

inline void MovableEntity::SetMaxTurnRate( float _fMaxTurnRate ) {
    m_fMaxTurnRate = _fMaxTurnRate;
}

inline float MovableEntity::GetMaxTurnRate() const {
    return m_fMaxTurnRate;
}

inline void MovableEntity::SetTextureFollowDirection( bool _bTextureFollowDirection ) {
    m_bTextureFollowDirection = _bTextureFollowDirection;
}

inline void MovableEntity::SetSmoothingOn( bool _IsSmoothingOn ) {
    m_bIsSmoothingOn = _IsSmoothingOn;
}

inline bool MovableEntity::IsSmoothingOn() const {
    return m_bIsSmoothingOn;
}

    } // namespace core
} // namespace smeg

#include <SmegTools/Debug/DebugNewOff.hpp>

#endif // MOVABLE_ENTITY_HPP
